package main

import (
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	"regexp"
	"strings"
)

// *************************编译器前端开始***************************

// Lexer 词法分析器
const (
	TOKEN_EOF = iota	// end of file
	TOKEN_VAR_PREFIX	// $
	TOKEN_LEFT_PAREN	// (
	TOKEN_RIGHT_PAREN	// )
	TOKEN_EQUAL			// =
	TOKEN_QUOTE			// "
	TOKEN_DUOQUOTE		// ""
	TOKEN_NAME			// variable name Name ::= [_A-Za-z][_0-9A-Za-z]*
	TOKEN_PRINT			// print
	TOKEN_IGNORED
)

type Lexer struct {
	sourceCode string
	lineNum int
	nextToken string
	nextTokenType int
	nextTokenLineNum int
}

type Variable struct {
	LineNum int
	Name string
}

type Print struct {
	LineNum int
	Variable *Variable
}

type Assignment struct {
	LineNum int
	Variable *Variable
	String string
}

type Statement interface {}

type SourceCode struct {
	LineNum int
	Statements []Statement
}

var tokenNameMap = map[int]string{
    TOKEN_EOF           : "EOF",
    TOKEN_VAR_PREFIX    : "$",
    TOKEN_LEFT_PAREN    : "(",
    TOKEN_RIGHT_PAREN   : ")",    
    TOKEN_EQUAL         : "=",
    TOKEN_QUOTE         : "\"",
    TOKEN_DUOQUOTE      : "\"\"",
    TOKEN_NAME          : "Name",
    TOKEN_PRINT         : "print",
    TOKEN_IGNORED       : "Ignored",
}

var keywords = map[string]int{
    "print"        : TOKEN_PRINT,
}

var regexpName = regexp.MustCompile(`^[_\d\w]+`)

func (lexer *Lexer) scanName() string {
	return lexer.scan(regexpName)
}

func (lexer *Lexer) scan(regexp *regexp.Regexp) string {
	if token := regexp.FindString(lexer.sourceCode); token != "" {
		lexer.skipSourceCode(len(token))
		return token
	}
	panic("unreachable!")
}

func (lexer *Lexer) isIgnored() bool {
	isIgnored := false
	isNewLine := func(c byte) bool {
		return c == '\r' || c == '\n'
	}
	isWhiteSpace := func(c byte) bool {
		switch c {
		case '\t', '\n', '\v', '\f', '\r', ' ':
			return true
		}
		return false
	}
	for len(lexer.sourceCode) > 0 {
		if lexer.nextSourceCodeIs("\r\n") || lexer.nextSourceCodeIs("\n\r") {
			lexer.skipSourceCode(2)
			lexer.lineNum += 1
			isIgnored = true
		} else if isNewLine(lexer.sourceCode[0]) {
			lexer.skipSourceCode(1)
			lexer.lineNum += 1
			isIgnored = true
		} else if isWhiteSpace(lexer.sourceCode[0]) {
			lexer.skipSourceCode(1)			
			isIgnored = true
		} else {
			break
		}
	}
	return isIgnored
}

func (lexer *Lexer) scanBeforeToken(token string) string {
	s := strings.Split(lexer.sourceCode, token)
	if len(s) < 2 {
		panic("unreachable")
	}
	lexer.skipSourceCode(len(s[0]))
	return s[0]
}

func NewLexer(sourceCode string) (*Lexer) {
	return &Lexer{sourceCode, 1, "", 0, 0}
}

func isLetter(c byte) bool {
	return c >= 'a' && c <= 'z' || c>= 'A' && c <= 'Z'
}

func (lexer *Lexer) skipSourceCode(n int) {
	lexer.sourceCode = lexer.sourceCode[n:]
}

func (lexer *Lexer) nextSourceCodeIs(s string) bool {
	return strings.HasPrefix(lexer.sourceCode, s)
}

func (lexer *Lexer) GetNextToken() (lineNum int, tokenType int, token string) {
	if lexer.nextTokenLineNum > 0 {
		lineNum = lexer.nextTokenLineNum
		tokenType = lexer.nextTokenType
		token = lexer.nextToken
		lexer.lineNum = lexer.nextTokenLineNum
		lexer.nextTokenLineNum = 0
		return
	}
	return lexer.MatchToken()
}

func (lexer *Lexer) MatchToken() (lineNum int, tokenType int, token string) {
	if lexer.isIgnored() {
		return lexer.lineNum, TOKEN_IGNORED, tokenNameMap[TOKEN_IGNORED]
	}

	if len(lexer.sourceCode) == 0 {
		return lexer.lineNum, TOKEN_EOF, tokenNameMap[TOKEN_EOF]
	}
	
	switch lexer.sourceCode[0] {
	case '$':
		lexer.skipSourceCode(1)
		return lexer.lineNum, TOKEN_VAR_PREFIX, tokenNameMap[TOKEN_VAR_PREFIX]
	case '(':
		lexer.skipSourceCode(1)
		return lexer.lineNum, TOKEN_LEFT_PAREN, tokenNameMap[TOKEN_LEFT_PAREN]
	case ')':
		lexer.skipSourceCode(1)
		return lexer.lineNum, TOKEN_RIGHT_PAREN, tokenNameMap[TOKEN_RIGHT_PAREN]
	case '=':
		lexer.skipSourceCode(1)
		return lexer.lineNum, TOKEN_EQUAL, tokenNameMap[TOKEN_EQUAL]
	case '"':
		if lexer.nextSourceCodeIs(tokenNameMap[TOKEN_DUOQUOTE]) {
			lexer.skipSourceCode(2)
			return lexer.lineNum, TOKEN_DUOQUOTE, tokenNameMap[TOKEN_DUOQUOTE]
		}
		lexer.skipSourceCode(1)
		return lexer.lineNum, TOKEN_QUOTE, tokenNameMap[TOKEN_QUOTE]
	}

	if lexer.sourceCode[0] == '_' || isLetter(lexer.sourceCode[0]) {
		token := lexer.scanName()
		if tokenType, isMatch := keywords[token]; isMatch {
			return lexer.lineNum, tokenType, token
		} else {
			return lexer.lineNum, TOKEN_NAME, token
		}
	}

	err := fmt.Sprintf("MatchToken(): error, unexptectd symbol near %q.", lexer.sourceCode)
	panic(err)
}

func (lexer *Lexer)NextTokenIs(tokenType int) (lineNum int, token string) {
	nowLineNum, nowTokenType, nowToken := lexer.GetNextToken()
	// print(nowToken, "******", lexer.lineNum, "*******", lexer.sourceCode)
	if tokenType != nowTokenType {
		err := fmt.Sprintf("NextTokenIs(): syntax error near '%s', expected token: {%s} but got {%s}.", tokenNameMap[nowTokenType], tokenNameMap[tokenType], tokenNameMap[nowTokenType])
		panic(err)
	}
	return nowLineNum, nowToken
}

func (lexer *Lexer)GetLineNum() (int) {
	return lexer.lineNum
}

func (lexer *Lexer)LookAhead() (int) {
	if lexer.nextTokenLineNum > 0 {
		return lexer.nextTokenType
	}
	nowLineNum := lexer.lineNum
	lineNum, tokenType, token := lexer.GetNextToken()
	// 恢复原来的值
	lexer.lineNum = nowLineNum
	lexer.nextTokenLineNum = lineNum
	lexer.nextTokenType = tokenType
	lexer.nextToken = token
	return tokenType
}

func (lexer *Lexer)LookAheadAndSkip(expectedType int) {
	nowLineNum := lexer.lineNum
	lineNum, tokenType, token := lexer.GetNextToken()
	if tokenType != expectedType {
		lexer.lineNum = nowLineNum
		lexer.nextTokenLineNum = lineNum
		lexer.nextTokenType = tokenType
		lexer.nextToken = token
	}
}

// Parser 语法解析器

// Name ::= [_A-Za-z][_0-9A-Za-z]*
func parseName(lexer *Lexer) (string, error) {
	_, name := lexer.NextTokenIs(TOKEN_NAME)
	return name, nil
}

// String ::= '"' '"' Ignored | '"' StringCharacter '"' Ignored
func parseString(lexer *Lexer) (string ,error) {
	str := ""
	switch lexer.LookAhead() {
	case TOKEN_DUOQUOTE:
		lexer.NextTokenIs(TOKEN_DUOQUOTE)
		lexer.LookAheadAndSkip(TOKEN_IGNORED)
		return str, nil
	case TOKEN_QUOTE:
		lexer.NextTokenIs(TOKEN_QUOTE)
		str = lexer.scanBeforeToken(tokenNameMap[TOKEN_QUOTE])
		lexer.NextTokenIs(TOKEN_QUOTE)
		lexer.LookAheadAndSkip(TOKEN_IGNORED)
		return str, nil
	default:
		return "", errors.New("parseString(): error, not a string.")
	}
}

func parsePrint(lexer *Lexer) (*Print, error) {
	var print Print
	var err error

	print.LineNum = lexer.GetLineNum()
	lexer.NextTokenIs(TOKEN_PRINT)
	lexer.NextTokenIs(TOKEN_LEFT_PAREN)
	lexer.LookAheadAndSkip(TOKEN_IGNORED)
	if print.Variable, err = parseVariable(lexer); err != nil {
		return nil, err
	}
	lexer.LookAheadAndSkip(TOKEN_IGNORED)
	lexer.NextTokenIs(TOKEN_RIGHT_PAREN)
	lexer.LookAheadAndSkip(TOKEN_IGNORED)
	return &print, nil
}

func parseAssignment(lexer *Lexer) (*Assignment, error) {
	var assignment Assignment
	var err error

	assignment.LineNum = lexer.GetLineNum()
	if assignment.Variable, err = parseVariable(lexer); err != nil {
		return nil, err
	}
	lexer.LookAheadAndSkip(TOKEN_IGNORED)
	lexer.NextTokenIs(TOKEN_EQUAL)
	lexer.LookAheadAndSkip(TOKEN_IGNORED)
	if assignment.String ,err = parseString(lexer); err != nil {
		return nil, err
	}
	lexer.LookAheadAndSkip(TOKEN_IGNORED)
	return &assignment, nil
}

func parseVariable(lexer *Lexer) (*Variable, error) {
	var variable Variable
	var err error
	variable.LineNum = lexer.GetLineNum()
	lexer.NextTokenIs(TOKEN_VAR_PREFIX)
	if variable.Name, err = parseName(lexer); err != nil {
		return nil, err
	}
	lexer.LookAheadAndSkip(TOKEN_IGNORED)
	return &variable, nil
}

func parseStatement(lexer *Lexer) (Statement, error) {
	lexer.LookAheadAndSkip(TOKEN_IGNORED)
	switch lexer.LookAhead() {
	case TOKEN_PRINT: // print
		return parsePrint(lexer)
	case TOKEN_VAR_PREFIX: // $
		return parseAssignment(lexer)
	default:
		return nil, errors.New("parseStatement(): error, unknow statement")
	}
}

func parseStatements(lexer * Lexer) ([]Statement, error) {
	var statements []Statement

	for !isSourceCodeEnd(lexer.LookAhead()) {
		var statement Statement
		var err error
		if statement, err = parseStatement(lexer); err != nil {
			return nil, err
		}
		statements = append(statements, statement)
	}
	return statements, nil
}

func isSourceCodeEnd(token int) bool {
	if token == TOKEN_EOF {
		return true
	}
	return false
}

func parseSourceCode(lexer *Lexer) (*SourceCode, error) {
	var sourceCode SourceCode
	var err error

	sourceCode.LineNum = lexer.GetLineNum()
	if sourceCode.Statements, err = parseStatements(lexer); err != nil {
		return nil, err
	}
	return &sourceCode, err
}

func parse(code string) (*SourceCode, error) {
	var sourceCode *SourceCode
	var err error

	lexer := NewLexer(code)
	if sourceCode, err = parseSourceCode(lexer); err != nil {
		return nil, err
	}
	lexer.NextTokenIs(TOKEN_EOF)
	return sourceCode, nil
}

// *************************编译器前端结束***************************
// *************************编译器后端开始***************************

// 变量存储区 
type GlobalVariables struct {
	Variables map[string]string
}

func NewGlobalVariables() *GlobalVariables {
	var g GlobalVariables
	g.Variables = make(map[string]string)
	return &g
}

func Execute(code string) {
	var ast *SourceCode
	var err error

	g := NewGlobalVariables()

	if ast, err = parse(code); err != nil {
		panic(err)
	}

	if err = resolveAST(g, ast); err != nil {
		panic(err)
	}
}

func resolveAST(g *GlobalVariables, ast *SourceCode) error {
	if len(ast.Statements) == 0 {
		return errors.New("resolveAST(): error, no code to execute, please check your input.")
	}
	for _, statement := range(ast.Statements) {
		if err := resolveStatement(g, statement); err != nil {
			return err
		}
	}
	return nil
}

func resolveStatement(g *GlobalVariables, statement Statement) error {
	if assignment, ok := statement.(*Assignment); ok {
		return resolveAssignment(g, assignment)
	} else if print, ok := statement.(*Print); ok {
		return resolvePrint(g, print)
	} else {
		return errors.New("resolveStatement(): error, undefined statement type")
	}
}

func resolveAssignment(g *GlobalVariables, assignment *Assignment) error {
	varName := ""
	if varName = assignment.Variable.Name; varName == "" {
		return errors.New("resolveAssignment(): error, variable name can not be empty.")
	}
	g.Variables[varName] = assignment.String
	return nil
}

func resolvePrint(g *GlobalVariables, print *Print) error {
	varName := ""
	if varName = print.Variable.Name; varName == "" {
		return errors.New("resolvePrint(): error, variable can not be empty.")
	}

	for k, v := range g.Variables {
		fmt.Println(k, " = ", v)
	}

	str := ""
	ok := false
	if str, ok = g.Variables[varName]; !ok {
		return errors.New(fmt.Sprintf("resolvePrint(): variable '$%s'not found.", varName))
	}
	fmt.Println(str)
	return nil
}

// *************************编译器后端结束***************************

// ===========================入口函数==============================
func main() {
	args := os.Args
	if len(args) != 2 {
		fmt.Printf("Usage: %s filename", args[0])
		return
	}
	filename := args[1]
	if filename == "" {
		filename = "./hello"
	}
	
	code, err := ioutil.ReadFile(filename)
	if err != nil {
		fmt.Printf("Error: Read file %s", filename)
		return
	}
	Execute(string(code))
}